package org.jiamandan.com.config;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelId;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import lombok.extern.slf4j.Slf4j;
import org.jiamandan.com.cache.CacheNames;
import org.jiamandan.com.dto.ZDBasicDto;
import org.jiamandan.com.strategy.ZDListenerContextService;
import org.jiamandan.com.utils.*;

import java.util.HashMap;
import java.util.Map;

/**
 * @version Revision 1.0.1
 * @版权：版权所有 (c) 2021
 * @see:
 * @创建日期：2022-10-21 15:20
 * @功能说明：Socket拦截器，用于处理客户端的行为
 * @begin
 * @修改记录:
 * @修改后版本 修改人        修改内容
 * @2022-10-21 15:20	         Lee        	创建
 * @end
 */
@Slf4j
public class NettyHandler extends ChannelInboundHandlerAdapter {

    public static final ChannelGroup clients = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    ZDListenerContextService contextService = SpringUtil.getBean(ZDListenerContextService.class);
    RedisUtil redisUtil = SpringUtil.getBean(RedisUtil.class);

    /**
     * 读取到客户端发来的消息
     *
     * @param ctx ChannelHandlerContext
     * @param msg msg
     * @throws Exception e
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        // 由于我们配置的是 字节数组 编解码器，所以这里取到的用户发来的数据是 byte数组
        byte[] data = (byte[]) msg;
        log.info("收到消息转16进制: " + CommonUtil.bytesToHex(data));
        log.info("发送消息的channelId: "+ctx.channel().id().asShortText());
        Channel client = ctx.channel();
        String payload = CommonUtil.bytesToHex(data);
        if(payload.equals("6c696e6b")){
            log.error("防中国移动踢掉网的，长度固定为4字节: " + payload);
            return;
        }
        if(payload.startsWith("38")){
            log.error("嘉满电-YD 这是通信模块每次连上socket时，都会（第一时间）发送一次sim卡号给socket: " + payload);
            return;
        }
        ZDBasicDto dto = ZDObjectUtil.parse(payload);
        if (dto == null) {
            log.error("接收到非法报文 : " + payload);
            return;
        }
        //获取到设备编号
        String deviceNo = null;
        if(dto.getStartPrefix().equals("68")){
            deviceNo =  CommonUtil.hexToString(dto.getDeviceNo()).trim();
        }else{
            deviceNo = StrUtil.toString(Integer.parseInt(CommonUtil.reverseHex(dto.getDeviceNo()).substring(2),16));
        }
        if (getChannelId(deviceNo,ctx.channel().id().asShortText(),dto.getCmd()) == null) {
            updateCacheByDeviceNo(deviceNo, client.id());
        }
        contextService.execute(dto, client);
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        log.info("新的客户端链接：" + ctx.channel().id().asShortText());
        clients.add(ctx.channel());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        log.info("断开链接：" + ctx.channel().id().asShortText());
        clients.remove(ctx.channel());
        removeCacheByChannelId(ctx.channel().id());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.info("链接报错：" + ctx.channel().id().asShortText());
        cause.printStackTrace();
        ctx.channel().close();
        clients.remove(ctx.channel());
        removeCacheByChannelId(ctx.channel().id());
    }

    /**
     * 获取channelId
     *
     * @param deviceNo
     * @return
     */
    private ChannelId getChannelId(String deviceNo,String oldChannelId,String cmd) {
        Map<String, ChannelId> result = (Map<String, ChannelId> ) redisUtil.get(CacheNames.TCP_CHANNEL);
        if (result == null || result.get(deviceNo) == null) {
            return null;
        }
        ChannelId channelId = result.get(deviceNo);
        log.info("当前缓存channelId：【"+channelId+"】接收消息的channelId:【"+oldChannelId+"】");
        if (cmd.equals("0200")) {
            if(!oldChannelId.equals(channelId.asShortText())){
                log.info("移除旧连接");
                clients.remove(channelId);
                this.removeCacheByChannelId(channelId);
                return null;
            }
        }
        if (clients.find(channelId) != null) {
            return channelId;
        } else {
            removeCacheByChannelId(channelId);
            return null;
        }


    }

    /**
     * @param deviceNo
     * @param channelId
     */
    private void updateCacheByDeviceNo(String deviceNo, ChannelId channelId) {
        Map<String, ChannelId> result = (Map<String, ChannelId> ) redisUtil.get(CacheNames.TCP_CHANNEL);
        if (result == null) {
            result = new HashMap<>();
        }
        result.put(deviceNo, channelId);
        log.info("=========="+channelId+"===============");
        log.info(result.toString());
        redisUtil.set(CacheNames.TCP_CHANNEL, result);
    }

    /**
     * @param channelId
     */
    private void removeCacheByChannelId(ChannelId channelId) {
        Map<String, ChannelId> result = (Map<String, ChannelId> ) redisUtil.get(CacheNames.TCP_CHANNEL);
        if (result == null) {
            return;
        }
        for (Map.Entry<String, ChannelId> entry : result.entrySet()) {
            if (entry.getValue().asShortText().equals(channelId.asShortText())) {
                result.remove(entry.getKey());
                break;
            }
        }
        redisUtil.set(CacheNames.TCP_CHANNEL, result);
    }
}
